<?php
/*--------------------------------------------------------------
   HtmlCacheReader.php 2020-12-28
   Gambio GmbH
   http://www.gambio.de
   Copyright (c) 2020 Gambio GmbH
   Released under the GNU General Public License (Version 2)
   [http://www.gnu.org/licenses/gpl-2.0.html]
 -------------------------------------------------------------*/

declare(strict_types=1);

namespace Gambio\Admin\Modules\Dashboard\Html;

use Curl\Curl;
use Gambio\Admin\Modules\Dashboard\Html\ValueObjects\CacheFilePath;
use Gambio\Admin\Modules\Dashboard\Html\ValueObjects\EndpointUrl;
use Throwable;

/**
 * Class HtmlCacheReader
 * @package Gambio\Admin\Modules\Dashboard\Html
 */
class HtmlCacheReader extends HtmlProvider
{
    protected const CACHE_FILE_TTL_IN_MINUTES = 15;
    
    /**
     * @var CacheFilePath
     */
    protected $cacheFilePath;
    
    
    /**
     * HtmlCacheReader constructor.
     *
     * @param EndpointUrl   $endpointUrl
     * @param Curl          $curl
     * @param CacheFilePath $cacheFilePath
     */
    public function __construct(
        EndpointUrl $endpointUrl,
        Curl $curl,
        CacheFilePath $cacheFilePath
    ) {
        parent::__construct($endpointUrl, $curl);
        $this->cacheFilePath = $cacheFilePath;
    }
    
    
    /**
     * @return string
     * @throws Throwable
     */
    public function dashboardHtml(): string
    {
        if ($this->isCacheValid()) {
            return file_get_contents($this->cacheFilePath->value());
        }

        return $this->fetchAndCache();
    }


    /**
     * Checks if the cache file exists and is still valid.
     *
     * @return bool
     */
    private function isCacheValid(): bool
    {
        return file_exists($this->cacheFilePath->value())
            && time() - filemtime($this->cacheFilePath->value()) < self::CACHE_FILE_TTL_IN_MINUTES * 60;
    }


    /**
     * Fetches content from remote and caches it. Falls back to stale cache on failure.
     *
     * @return string
     * @throws Throwable
     */
    private function fetchAndCache(): string
    {
        try {
            $result = parent::dashboardHtml();

            if ($result !== null && $result !== '') {
                file_put_contents($this->cacheFilePath->value(), $result);
                return $result;
            }
        } catch (Throwable $exception) {
            return $this->getStaleCache($exception);
        }

        return $this->getStaleCache();
    }


    /**
     * Returns stale cache content if available, otherwise throws exception or returns empty string.
     *
     * @param Throwable|null $exception
     *
     * @return string
     * @throws Throwable
     */
    private function getStaleCache(?Throwable $exception = null): string
    {
        if (file_exists($this->cacheFilePath->value())) {
            touch($this->cacheFilePath->value());
            return file_get_contents($this->cacheFilePath->value());
        }

        if ($exception !== null) {
            throw $exception;
        }

        return '';
    }
}